package rabbit.deal;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @Author: sjw
 * @Description: RSA非对称加密工具类
 * @Date: 2018/6/29 18:34
 */

public class RSAHelper {

	static final String RSA_KEY_ALGORITHM = "RSA";
	static final String RSA_CIPHER_ALGORITHM = "RSA";//"RSA/ECB/PKCS1Padding";
	static final int MAX_ENCRYPT_BLOCK = 117;
	static final int MAX_DECRYPT_BLOCK = 128;

	/**
	 * 生成密钥
	 *
	 * @return
	 */
	public static String generatorKeyPair() {
		String result = null;
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA_KEY_ALGORITHM);
			keyPairGen.initialize(1024);
			KeyPair keyPair = keyPairGen.generateKeyPair();
			RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
			byte[] keyBs = rsaPublicKey.getEncoded();
			String publicKey = new String(Base64Helper.encode(keyBs), "UTF-8");
			result = "生成的公钥：\r" + publicKey;
			RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
			keyBs = rsaPrivateKey.getEncoded();
			String privateKey = new String(Base64Helper.encode(keyBs), "UTF-8");
			result += "\n生成的私钥：\r" + privateKey;
     		System.out.println(result);
		} catch (Exception e) {
			e.printStackTrace();
			result = null;
		}
		return result;
	}

	/**
	 * 获取公钥
	 *
	 * @param publicKey
	 * @return
	 */
	private static PublicKey getPublicKey(String publicKey) {
		try {
			X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64Helper.decode(publicKey, "UTF-8"));
			KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
			return keyFactory.generatePublic(publicKeySpec);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 获取私钥
	 *
	 * @param privateKey
	 * @return
	 */
	private static PrivateKey getPrivateKey(String privateKey) {
		try {
			PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64Helper.decode(privateKey, "UTF-8"));
			KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
			return keyFactory.generatePrivate(privateKeySpec);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 分段处理
	 *
	 * @param cipher
	 * @param inData
	 * @param outData
	 * @param blockMax
	 * @throws BadPaddingException
	 * @throws IllegalBlockSizeException
	 */
	private static void rsaBlockCode(Cipher cipher, byte[] inData, ByteArrayOutputStream outData, int blockMax) throws BadPaddingException, IllegalBlockSizeException {
		int offSet = 0;
		byte[] cache;
		int i = 0;
		int inputLen = inData.length;
		// 对数据分段加密/解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > blockMax) {
				cache = cipher.doFinal(inData, offSet, blockMax);
			} else {
				cache = cipher.doFinal(inData, offSet, inputLen - offSet);
			}
			outData.write(cache, 0, cache.length);
			i++;
			offSet = i * blockMax;
		}
	}

	/**
	 * 公钥加密
	 *
	 * @param data
	 * @param publicKeyStr
	 * @return
	 */
	public static String encryptionByPublicKey(String data, String publicKeyStr) {
		String result = null;
		try {
			PublicKey publicKey = getPublicKey(publicKeyStr);
			Cipher cipher = Cipher.getInstance(RSA_CIPHER_ALGORITHM);//publicKey.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			//cipher.update(data.getBytes("UTF-8"));
			//result = new String(Base64.encodeBase64(cipher.doFinal()), "UTF-8");

			byte[] bytes = data.getBytes("UTF-8");
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			rsaBlockCode(cipher, bytes, out, MAX_ENCRYPT_BLOCK);
			byte[] encryptedData = out.toByteArray();
			result = new String(Base64Helper.encode(encryptedData), "UTF-8");
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 私钥解密
	 *
	 * @param data
	 * @param privateKeyStr
	 * @return
	 */

	public static String decryptionByPrivateKey(String data, String privateKeyStr) {
		String result = null;
		try {
			PrivateKey privateKey = getPrivateKey(privateKeyStr);
			Cipher cipher = Cipher.getInstance(RSA_CIPHER_ALGORITHM);//privateKey.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			//cipher.update(Base64.decodeBase64(data.getBytes("UTF-8")));
			//result = new String(cipher.doFinal(), "UTF-8");

			byte[] encryptedData = Base64Helper.decode(data, "UTF-8");
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			rsaBlockCode(cipher, encryptedData, out, MAX_DECRYPT_BLOCK);
			byte[] decryptedData = out.toByteArray();
			result = new String(decryptedData, "UTF-8");
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 私钥加密
	 *
	 * @param data
	 * @param privateKeyStr
	 * @return
	 */
	public static String encryptionByPrivateKey(String data, String privateKeyStr) {
		String result = null;
		try {
			PrivateKey privateKey = getPrivateKey(privateKeyStr);
			Cipher cipher = Cipher.getInstance(RSA_CIPHER_ALGORITHM);//publicKey.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, privateKey);
			//cipher.update(data.getBytes("UTF-8"));
			//result = new String(Base64.encodeBase64(cipher.doFinal()), "UTF-8");

			byte[] bytes = data.getBytes("UTF-8");
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			rsaBlockCode(cipher, bytes, out, MAX_ENCRYPT_BLOCK);
			byte[] encryptedData = out.toByteArray();
			result = new String(Base64Helper.encode(encryptedData), "UTF-8");
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

    /**
     * 公钥解密
     *
     * @param data
     * @param publicKeyStr
     * @return
     */

    public static String decryptionByPublicKey(String data, String publicKeyStr) {
        String result = null;
        try {
            PublicKey publicKey = getPublicKey(publicKeyStr);
            Cipher cipher = Cipher.getInstance(RSA_CIPHER_ALGORITHM);//privateKey.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            //cipher.update(Base64.decodeBase64(data.getBytes("UTF-8")));
            //result = new String(cipher.doFinal(), "UTF-8");

            byte[] encryptedData = Base64Helper.decode(data, "UTF-8");
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            rsaBlockCode(cipher, encryptedData, out, MAX_DECRYPT_BLOCK);
            byte[] decryptedData = out.toByteArray();
            result = new String(decryptedData, "UTF-8");
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
